package es.uc3m.setichat.android;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import es.uc3m.setichat.utils.Base64;

import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.os.Environment;
import android.util.Log;


public class SeTIChatCertificatesController {
	public static Context context 			= null;
	public FileInputStream iStream 			= null;
	public FileInputStream iStream_private	= null;
	public static PrivateKey private_key	= null;
	
	public static void initPrivateKey(){
		SeTIChatSQLiteHelper setiDB = new SeTIChatSQLiteHelper(SeTIChatCertificatesController.context, "BBDDSetichat", null, SeTIChatSQLiteHelper.SQL_VERSION);
		
		SQLiteDatabase db = setiDB.getReadableDatabase();
        Cursor c = db.query("user_certificates",new String[]{"certificate","password"},"type='p12'", null,null,null,null);
    	if(c.moveToFirst()){
			KeyStore keystore;
			try {
				keystore = KeyStore.getInstance("PKCS12");
				byte certificate[] = Base64.decode(c.getString(0));
				keystore.load(new ByteArrayInputStream(certificate), c.getString(1).toCharArray()); 
				if(keystore.aliases().hasMoreElements()){
					String alias = keystore.aliases().nextElement();
					SeTIChatCertificatesController.private_key = (PrivateKey) keystore.getKey(alias, c.getString(1).toCharArray()); 
				}
			} catch (KeyStoreException e) {
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
			} catch (CertificateException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (UnrecoverableKeyException e) {
				e.printStackTrace();
			}
		
    	}
    	c.close();
    	db.close();
    	setiDB.close();
	}
	
	
	public static String verifyCertificate(X509Certificate cert){
		
		X509Certificate ca = null;
		PublicKey caKey = null;
		AssetManager assetManager = SeTIChatCertificatesController.context.getAssets();
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			
			ca = (X509Certificate) cf.generateCertificate(assetManager.open("CA.cer"));
			
			caKey = ca.getPublicKey();
			
			ca.checkValidity(); 	//validamos si la fecha del certificado es correcta
			ca.verify(caKey);		//verificamos que este firmado por si mismo
			
			
					
		} catch (CertificateExpiredException e){	
			return "ca certificate expired";
		} catch (CertificateNotYetValidException e){
			return "ca certificate not yet valid";
		} catch (CertificateException e) {
			return "ca certificate invalid";
		} catch (InvalidKeyException e) {
			return "ca certificate invalid";
		} catch (NoSuchAlgorithmException e) {
			return "ca certificate invalid";
		} catch (NoSuchProviderException e) {
			return "ca certificate invalid";
		} catch (SignatureException e) {
			return "ca certificate invalid";
		} catch (IOException e) {
			return "ca certificate invalid";
		}
		
		try {
			cert.checkValidity();	//validamos si la fecha del certificado es correcta
			cert.verify(caKey);		//verificamos que este firmado por la CA
		} catch (CertificateExpiredException e) {
			return "user certificate expired";
		} catch (CertificateNotYetValidException e) {
			return "user certificate not yet valid";
		} catch (InvalidKeyException e) {
			return "user certificate invalid";
		} catch (CertificateException e) {
			return "user certificate invalid";
		} catch (NoSuchAlgorithmException e) {
			return "user certificate invalid";
		} catch (NoSuchProviderException e) {
			return "user certificate invalid";
		} catch (SignatureException e) {
			return "user certificate invalid";
		}
		
		
		return "certificate valid";
	}
	
	public static boolean checkSignature(String message, String signature, X509Certificate cert){
		
		String aux = message.substring(message.indexOf("<header>"), message.indexOf("</content>") + 10);
		Log.d("validate",aux);
		Log.d("validate",signature);
		
		try {
			Signature sign = Signature.getInstance("SHA1withRSA");
			sign.initVerify(cert.getPublicKey());
			sign.update(aux.getBytes("UTF-8"));
			return sign.verify(Base64.decode(signature));
			
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (SignatureException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		return false;
	}
	
	public static String signed(String message){
		String aux = message.substring(message.indexOf("<header>"), message.indexOf("</content>") + 10);
		
		try {
			Signature sign = Signature.getInstance("SHA1withRSA");
				
			sign.initSign(private_key);
			sign.update(aux.getBytes("UTF-8"));
			String signature = Base64.encodeToString(sign.sign(), false);
			
			return signature;
    		
			
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (SignatureException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static String encrypted(String message, X509Certificate cert){
		
		try {
			byte iv[] = SeTIChatCertificatesController.generateIV();
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			kgen.init(128); 
			SecretKey skey = kgen.generateKey();
			Cipher SymKeyCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			SecretKeySpec aesKeySpec = new SecretKeySpec(skey.getEncoded(), "AES");
			SymKeyCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec,  new IvParameterSpec(iv));
			byte encryptedMessage[] = SymKeyCipher.doFinal(message.getBytes("UTF-8"));
			
			Cipher publicKeyCipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
			publicKeyCipher.init(Cipher.ENCRYPT_MODE, cert);
			byte encryptedKey[] = publicKeyCipher.doFinal(aesKeySpec.getEncoded());
			
			byte rsa_iv_message[] = new byte[ 272 + encryptedMessage.length];
			System.arraycopy(encryptedKey, 0, rsa_iv_message, 0, 256);
			System.arraycopy(iv, 0, rsa_iv_message, 256, 16);
			System.arraycopy(encryptedMessage, 0, rsa_iv_message, 272, encryptedMessage.length);
			
			return Base64.encodeToString(rsa_iv_message, false);
			
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		
		return "";
	}
	
	public static String decrypted(String message){
		
		byte 	rsa_iv_message[] 	= Base64.decode(message),
				iv[] 				= new byte[16],
				encryptedKey[] 		= new byte[256],
				encryptedMessage[]	= new byte[rsa_iv_message.length - 272];
		
		System.arraycopy(rsa_iv_message, 0,  encryptedKey, 0, 256);
		System.arraycopy(rsa_iv_message, 256, iv, 0, 16);
		System.arraycopy(rsa_iv_message, 272, encryptedMessage, 0, encryptedMessage.length);
		
		try {
			Cipher publicKeyCipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");

			publicKeyCipher.init(Cipher.DECRYPT_MODE, private_key);
			byte aesKey[] = publicKeyCipher.doFinal(encryptedKey);
			
			Cipher SymKeyCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			SecretKeySpec aesKeySpec = new SecretKeySpec(aesKey, "AES");
			SymKeyCipher.init(Cipher.DECRYPT_MODE, aesKeySpec,  new IvParameterSpec(iv));
			byte decryptedMessage[] = SymKeyCipher.doFinal(encryptedMessage);
			
			return new String(decryptedMessage);
			
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		
		return "(No se a podido descifrar)";
	}
	
	public static byte[] generateIV(){
		Random rand = new Random(System.currentTimeMillis());
		int aux;
		byte result[] = new byte[16];
		for(int i=0;i<16;i++){
			aux = rand.nextInt(256);
			result[i] = (byte) aux;
		}
		return result;
	}
	
}
